<template>
  <div class="publish">
    <!-- 1.0 面包屑组件 - Breadcrumb 面包屑 -->
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>{{ this.id ? '文章编辑' : '文章发布' }}</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 2.0 卡片组件 - Card 卡片 -->
    <el-card class="box-card">
      <!-- 3.0 表单结构 -->
      <el-form
        ref="form"
        :model="form"
        label-width="80px"
        :rules="rules"
      >
        <!-- 收集数据1： 标题 title -->
        <el-form-item
          prop="title"
          label="标题"
        >
          <el-input v-model="form.title"></el-input>
        </el-form-item>
        <!-- 收集数据2：栏目 categories -->
        <el-form-item
          label="栏目"
          prop="categories"
        >
          <!-- 全选按钮 - 单独一个在外面 -->
          <el-checkbox
            :indeterminate="isIndeterminate"
            v-model="checkAll"
            @change="handleCheckAllChange"
          >全选</el-checkbox>
          <!-- 小选的编组 -->
          <el-checkbox-group v-model="form.categories">
            <!-- 小选列表 -->
            <el-checkbox
              v-for="item in cateList"
              :key="item.id"
              :label="item.id"
              name="categories"
            >{{ item.name }}</el-checkbox>
          </el-checkbox-group>
        </el-form-item>
        <!-- 收集数据3：类型 type -->
        <el-form-item label="类型">
          <el-radio-group v-model="form.type">
            <el-radio :label="1">文章</el-radio>
            <el-radio :label="2">视频</el-radio>
          </el-radio-group>
        </el-form-item>
        <!-- 收集数据4：内容 content -->
        <el-form-item label="内容">
          <!-- 4.1 使用 VueEditor - 收集文章内容 -->
          <VueEditor
            :config="config"
            ref="vueEditor"
            v-show="form.type === 1"
          />
          <!-- 4.2 使用上传组件 - 收集视频 -->
          <el-upload
            v-show="form.type === 2"
            :action="$baseURL + '/upload'"
            :headers="headers"
            :before-upload="videoBeforeUpload"
            :on-success="videoSuccess"
            :on-remove="videoRemove"
            :limit="1"
            :file-list="videoList"
          >
            <el-button
              size="small"
              type="primary"
            >点击上传视频</el-button>
            <div
              slot="tip"
              class="el-upload__tip"
            >只能上传mp4文件，且不超过1MB</div>
          </el-upload>
        </el-form-item>
        <!-- 收集数据5：封面 cover -->
        <el-form-item label="封面">
          <el-upload
            :action="$baseURL + '/upload'"
            list-type="picture-card"
            :headers="headers"
            :on-success="onSuccess"
            :on-remove="onRemove"
            :limit="3"
            :on-exceed="onExceed"
            :file-list="form.cover"
          >
            <i class="el-icon-plus"></i>
          </el-upload>
        </el-form-item>
        <el-form-item>
          <el-button
            type="primary"
            @click="onSubmit"
          >{{ this.id ? '编辑' : '提交'}}</el-button>
        </el-form-item>
      </el-form>
    </el-card>
  </div>
</template>

<script>
import { getCategoryAPI } from "@/apis/category";
// 富文本插件地址：https://github.com/hsian/vue-word-editor
// 1. 下包  yarn add vue-word-editor
// 2. 导入 VueEditor
import VueEditor from "vue-word-editor";
import "quill/dist/quill.snow.css";
import { getToken } from "@/utils/localData";
import {
  getPostDetailByIdAPI,
  publishPostAPI,
  updatePostAPI,
} from "@/apis/news";
export default {
  // 3. 注册 VueEditor
  components: {
    VueEditor,
  },
  data() {
    return {
      id: this.$route.params.id,
      // 用于提交到后端的表单数据
      form: {
        title: "", // 标题
        categories: [], // 栏目id
        type: 1, // 类型
        content: "", // 文章内容
        cover: [], // 封面图片ID集合
      },
      // 表单验证
      rules: {
        // 标题
        title: [
          { required: true, message: "请输入标题", trigger: "blur" },
          {
            min: 3,
            max: 20,
            message: "长度在 3 到 20 个字符",
            trigger: "blur",
          },
        ],
        // 栏目
        categories: [
          {
            type: "array",
            required: true,
            message: "请至少选择一个栏目",
            trigger: "change",
          },
        ],
      },
      headers: { Authorization: getToken() },
      cateList: [], // 栏目列表
      checkAll: false, // 全选状态
      isIndeterminate: false, // 不确定状态
      config: {
        // 上传图片的配置
        uploadImage: {
          // url，name，headers 都要按接口文档的来
          url: this.$baseURL + "/upload",
          name: "file",
          // 🔔 由于这里并不是 axios 发送的请求，所以需要独立配置一下请求头
          headers: { Authorization: getToken() },
          // res是结果，insert方法会把内容注入到编辑器中，res.data.url是资源地址
          // 💥 因为内部要用到 this，所以要改成箭头函数写法
          uploadSuccess: (res, insert) => {
            console.log(res);
            if (res.data.message === "文件上传成功") {
              // 用于把图片插入到富文本编辑器中的(图片完整路径)
              insert(this.$baseURL + res.data.data.url);
            } else {
              this.$message.warning("文件上传有误");
            }
          },
        },
      },
      // 用于展示的视频列表
      videoList: [],
    };
  },
  async created() {
    // console.log(this.$baseURL);
    // 获取栏目数据
    const res = await getCategoryAPI();
    // console.log(res);
    this.cateList = res.data.data.slice(2);
    // console.log(this.$route.params.id);
    // console.log(this.id);
    // 如果路由参数中有id，就是编辑文章，需要先获取到原来的文章详情
    if (this.id) {
      const res2 = await getPostDetailByIdAPI(this.id);
      // console.log(res2);
      const articleObj = res2.data.data;
      // 根据路由的id获取到了文章详情
      if (articleObj) {
        // 把文章数据显示到页面中
        console.log("文章详情数据", articleObj);
        // 1. 标题
        this.form.title = articleObj.title;
        // 2. 选中的栏目
        this.form.categories = articleObj.categories.map((item) => item.id);
        // 3. 类型
        this.form.type = articleObj.type;
        // 4.内容
        this.form.content = articleObj.content;
        // 4.1 富文本内容，没有双向绑定 - 需要调用插件提供的 API 设置富文本内容
        if (this.form.type === 1) {
          // 🎃 设置富文本编辑器的内容 - 插件文档 CV 过来的
          // 手册地址：https://gitee.com/Megasu/vue-word-editor
          const quill = this.$refs.vueEditor.editor;
          quill.clipboard.dangerouslyPasteHTML(0, articleObj.content);
        } else {
          // 4.2 视频要按 Upload 上传插件 :file-list="[{ name:'', url: '' }]"
          this.videoList = [{ name: "mp4视频", url: articleObj.content }];
        }
        // 5. 封面
        this.form.cover = articleObj.cover.map((item) => {
          // 通过 map 处理成插件要求格式 [{ name:'', url: '' },{ name:'', url: '' }]
          return {
            id: item.id,
            name: item.id,
            // 拼接完整图片路径
            url: this.$baseURL + item.url,
          };
        });
      } else {
        // 如果根据id获取不到文章，就提示用户
        this.$message.error("文章不存在");
      }
    }
  },
  methods: {
    // 当视频被移除时
    videoRemove() {
      // 把视频数据清空一下
      this.form.content = "";
    },
    // 上传文件之前的钩子，参数为上传的文件，若返回 false 则停止上传。
    videoBeforeUpload(file) {
      console.log("当前文件对象是", file);
      // 🔔 视频只能上传 mp4 格式 并且 大小不能超过 1M
      if (file.type !== "video/mp4" || file.size >= 1 * 1024 * 1024) {
        // 提示用户
        this.$message.error("上传的文件不是 mp4 格式，或者超过了大小限制");
        // 返回 false , 可中止上传
        return false;
      }
    },
    // 视频上传成功的回调
    videoSuccess(res) {
      console.log(1111, "文件上传成功时的钩子", res);
      // 🔔上传文件可能失败，成功才追加到数组中
      if (res.message === "文件上传成功") {
        // 直接拼接好视频地址作为内容即可
        this.form.content = this.$baseURL + res.data.url;
      } else {
        this.$message.warning("上传出问题了");
      }
    },
    // 文件超出个数限制时的钩子
    onExceed() {
      this.$message.warning("封面不能超出3张哦");
    },
    // 文件上传成功时的钩子
    onSuccess(res) {
      console.log(1111, "文件上传成功时的钩子", res);
      // 🔔上传文件可能失败，成功才追加到数组中
      if (res.message === "文件上传成功") {
        // 封面数组也要按接口文档要求来
        this.form.cover.push({
          id: res.data.id,
          // name 和 url 用于 Upload 插件 file-list 属性渲染展示使用的
          name: res.data.id,
          // 拼接成完整路径才能看到图片
          url: this.$baseURL + res.data.url,
        });
      } else {
        this.$message.warning("上传出问题了");
      }
    },
    // 文件列表移除文件时的钩子
    // file: 当前被移除的
    onRemove(file) {
      console.log(2222, "文件列表移除文件时的钩子", file);
      // 🔔 获取到删除项的 id，没有 file.response 就获取 file.id
      const id = file.response ? file.response.data.id : file.id;
      // 图片删除了， id 也要被删除
      this.form.cover = this.form.cover.filter((item) => item.id !== id);
    },
    // 发布文章的按钮 - 💥 发布前一定要先检查数据，是否为空，字段是否和后端接口要求一致
    async onSubmit() {
      // 数据检查1 - 标题不能为空
      if (/^.{3,20}$/.test(this.form.title.trim()) === false) {
        return this.$message.warning("标题不符合规则");
      }
      // 数据检查2 - 栏目不能为空
      if (this.form.categories.length === 0) {
        return this.$message.warning("至少选择一个栏目");
      }
      // 数据检查3 - 封面不能为空
      if (this.form.cover.length === 0) {
        return this.$message.warning("至少上传一个封面");
      }
      // 如果是文章类型，type 为 1
      if (this.form.type == 1) {
        // 按照后端字段要求，保存到 content 字段中，为发布文章做准备
        this.form.content = this.$refs.vueEditor.editor.root.innerHTML;
        // 数据检查4 - 发布的内容不能为空
        if (this.form.content === "<p><br></p>") {
          return this.$message.warning("新闻内容不能为空");
        }
      } else if (this.form.type == 2 && this.form.content == "") {
        return this.$message.warning("请上传视频");
      }
      // 💥转换成后端所需的格式 - 栏目数据需要调整
      const data = {
        // 展开原本的用于页面绑定 form 表单数据
        ...this.form,
        // 由于 cover 用于做封面预览的时候会有一些多余的数据，后端其实只需要图片的 id 即可
        cover: this.form.cover.map((item) => {
          return { id: item.id };
        }),
        // 后端栏目数据要求 [{id:1}, {id:2}] 格式
        categories: this.form.categories.map((item) => {
          return { id: item };
        }),
      };
      console.log("💥当前用于发送请求数据为：", data);

      // 情况1：如果有id，就调用编辑接口
      if (this.id) {
        const res = await updatePostAPI(this.id, data);
        console.log(res);
        if (res.data.message === "文章编辑成功") {
          this.$message.success("文章编辑成功");
          // 发布成功后跳转到文章列表页
          this.$router.push("/home/post_list");
        }
      } else {
        // 情况2：如果没有id，就调用发布接口
        // 所有数据准备完毕，调用发布文章接口即可
        const res = await publishPostAPI(data);
        console.log(res);
        // 发布成功后的提示和页面跳转
        if (res.data.message === "文章发布成功") {
          // 根据类型给用户不同提示
          const tips = this.form.type === 1 ? "文章发布成功" : "视频发布成功";
          this.$message.success(tips);
          // 发布成功后跳转到文章列表页
          this.$router.push("/home/post_list");
        }
      }
    },
    // 全选按钮被点击时触发
    handleCheckAllChange(val) {
      console.log("获取全选当前的状态", val);
      // 如果值为 true ， map 处理后端返回的栏目数组
      this.form.categories =
        val === true ? this.cateList.map((item) => item.id) : [];
      // 取消不确定状态
      this.isIndeterminate = false;
    },
  },
  watch: {
    // Bug修复：由于编辑页面默认情况下没有人点击小选按钮导致全选状态异常显示问题
    // 👍 只监听对象中的某个数据的变化
    "form.categories"(value) {
      console.log("栏目数据发生了变化", value);
      // 当前选中栏目的长度
      let checkedCount = this.form.categories.length;
      // 全选状态：选中栏目的长度 和 原数组长度是否一致，说明全选
      this.checkAll = checkedCount === this.cateList.length;
      // 不确定状态：只选中了其中某一些  0 < ? < 总长度
      this.isIndeterminate =
        checkedCount > 0 && checkedCount < this.cateList.length;
    },
  },
};
</script>

<style lang="less" scoped>
.box-card {
  margin-top: 15px;
}
</style>